package org.microboot.data.utils;

import org.microboot.core.bean.ApplicationContextHolder;
import org.microboot.core.constant.Constant;
import org.microboot.core.func.FuncV1;
import org.microboot.data.factory.DataSourceFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.transaction.TransactionProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.time.Duration;

/**
 * @author 胡鹏
 */
public class TransactionManagerUtils {

    /**
     * 动态事务管理器
     *
     * @param dataSource
     */
    public static void dynamicTransactionManager(DataSource dataSource) throws Exception {
        dynamicTransactionManager(dataSource, null);
    }

    /**
     * 动态事务管理器
     *
     * @param dataSource
     */
    public static void dynamicTransactionManager(DataSource dataSource, FuncV1<BeanDefinitionBuilder> func) throws Exception {
        if (dataSource == null) {
            return;
        }

        BeanDefinitionRegistry beanDefinitionRegistry = getBeanDefinitionRegistry();
        if (beanDefinitionRegistry == null) {
            return;
        }

        /*
            本来想利用Spring的BeanDefinition继承来复用SpringBoot自动装配的TransactionManager的BeanDefinition
            但遇到了问题：
                1、SpringBoot自动装配的TransactionManager是通过transactionManager(...)方法创建的
                    它有两个参数，分别是：DataSource dataSource，ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers
                    DataSource是数据源，ObjectProvider是临时创建的一个DependencyObjectProvider对象，并不在Spring容器中管理
                2、通过调试可知，transactionManager创建Bean的流程走的是FactoryMethod的方式，即：instantiateUsingFactoryMethod()
                    instantiateUsingFactoryMethod()方法中不但完成Bean的实例化，同时还要完成Bean属性的赋值，这里给属性赋值与populateBean()方法不同
                    populateBean()是从BeanDefinition的propertyValues拿到属性值，可以通过addPropertyValue()添加属性值
                    instantiateUsingFactoryMethod()方法是从BeanDefinition的constructorArgumentValues拿到属性值，可以通过addConstructorArgValue()添加属性值
                3、在instantiateUsingFactoryMethod()方法中会判断BeanDefinition的constructorArgumentValues是否包含属性值
                    默认情况下是不包含的，因此transactionManagerCustomizers会创建一个对象进行赋值
                    但这里如果通过addConstructorArgValue()添加了DataSource，那么transactionManagerCustomizers就不会走创建流程了，而是从constructorArgumentValues获取
                    但transactionManagerCustomizers的创建流程比较复杂
            最终放弃继承BeanDefinition的方式，采用新建BeanDefinition的方式
            其实想采用继承BeanDefinition的方式创建Bean是因为transactionManagerCustomizers对TransactionManager做了一些操作
            查看源码发现最终是通过TransactionProperties给TransactionManager的defaultTimeout和rollbackOnCommitFailure进行赋值
         */
        //通过BeanDefinitionBuilder创建BeanDefinition
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(DataSourceTransactionManager.class);

        //设置dataSource
        beanDefinitionBuilder.addPropertyValue("dataSource", dataSource);

        //设置defaultTimeout和rollbackOnCommitFailure【参考SpringBoot默认事务管理器】
        TransactionProperties transactionProperties = ApplicationContextHolder.getBean(TransactionProperties.class);
        if (transactionProperties != null) {
            Duration defaultTimeout = transactionProperties.getDefaultTimeout();
            if (defaultTimeout != null) {
                beanDefinitionBuilder.addPropertyValue("defaultTimeout", (int) defaultTimeout.getSeconds());
            }
            Boolean rollbackOnCommitFailure = transactionProperties.getRollbackOnCommitFailure();
            if (rollbackOnCommitFailure != null) {
                beanDefinitionBuilder.addPropertyValue("rollbackOnCommitFailure", rollbackOnCommitFailure);
            }
        }

        //属性设置扩展
        if (func != null) {
            func.func(beanDefinitionBuilder);
        }

        //数据源名称
        String dataSourceName = ApplicationContextHolder.getBean(DataSourceFactory.class).getDataSourceName(dataSource);

        //事务名
        String transactionManagerName = Constant.TRANSACTION_MANAGER_PREFIX.concat(Constant.TRANSACTION_MANAGER_CONNECTOR).concat(dataSourceName);

        //注册bean
        beanDefinitionRegistry.registerBeanDefinition(transactionManagerName, beanDefinitionBuilder.getRawBeanDefinition());
    }

    /**
     * 获取BeanDefinitionRegistry
     *
     * @return
     */
    private static BeanDefinitionRegistry getBeanDefinitionRegistry() {
        ApplicationContext applicationContext = ApplicationContextHolder.getApplicationContext();
        if (!(applicationContext instanceof ConfigurableApplicationContext)) {
            return null;
        }
        //将ApplicationContext转换为ConfigurableApplicationContext
        ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) applicationContext;

        ConfigurableListableBeanFactory beanFactory = configurableApplicationContext.getBeanFactory();
        if (!(beanFactory instanceof BeanDefinitionRegistry)) {
            return null;
        }
        //将ConfigurableListableBeanFactory转换为BeanDefinitionRegistry
        return (BeanDefinitionRegistry) beanFactory;
    }
}